package com.example.wastewater.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.wastewater.common.biz.user.UserContext;
import com.example.wastewater.common.convention.exception.ClientException;
import com.example.wastewater.common.convention.exception.ServiceException;
import com.example.wastewater.dao.entity.UserDO;
import com.example.wastewater.dao.mapper.UserMapper;
import com.example.wastewater.dto.req.UserLoginReqDTO;
import com.example.wastewater.dto.req.UserRegisterReqDTO;
import com.example.wastewater.dto.req.UserUpdateReqDTO;
import com.example.wastewater.dto.resp.UserActualRespDTO;
import com.example.wastewater.dto.resp.UserLoginRespDTO;
import com.example.wastewater.service.IUserService;
import com.example.wastewater.toolkit.EmailValidator;
import com.example.wastewater.toolkit.RandomGenerator;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.example.wastewater.common.constant.RedisCacheConstant.USER_LOGIN_KEY;
import static com.example.wastewater.common.enums.UserErrorCode.*;


/**
 *  用户接口实现层
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements IUserService {

    private final StringRedisTemplate stringRedisTemplate;
    private final UserMapper userMapper;

    @Override
    public void register(UserRegisterReqDTO requestParam) {
        // 1、参数校验
        if(BeanUtil.isEmpty(requestParam)){
            throw new ClientException("参数有误");
        }
        EmailValidator emailValidator = new EmailValidator();
        if(!emailValidator.validate(requestParam.getEmail())){
            throw new ClientException("邮箱格式有误");
        }
        Integer roleId = requestParam.getRoleId();
        if(roleId == null || roleId < 1 || roleId > 4){
            throw new ClientException("权限id不合法");
        }

        // 2、查询邮箱是否存在
        UserDO userDO = lambdaQuery().eq(UserDO::getEmail, requestParam.getEmail()).one();
        if(userDO != null){
            throw new ClientException("邮箱已经存在");
        }

        // 3、存入数据库
        String username = requestParam.getUsername();
        if(username == null){
            username = RandomGenerator.generateRandomString(16);
        }
        userDO = UserDO.builder()
                .username(username)
                .email(requestParam.getEmail())
                .password(requestParam.getPassword())
                .roleId(requestParam.getRoleId())
                .build();
        save(userDO);
    }

    @Override
    public void userInfoUpdate(UserUpdateReqDTO requestParam, String token) {
        // 1、校验修改的用户信息是不是当前登录用户的信息
        String email = UserContext.getUserEmail();
        // 2、校验用户是否存在
        UserDO userDO = lambdaQuery().eq(UserDO::getEmail, email).one();
        if(userDO == null){
            throw new ClientException(USER_EMAIL_NOT_EXIST_ERROR);
        }
        UserDO userDO1 = lambdaQuery().eq(UserDO::getEmail, requestParam.getEmail()).one();
        if(userDO1 != null){
            throw new ClientException("要修改的邮箱已经存在");
        }
        // 3、修改
        LambdaUpdateWrapper<UserDO> wrapper = Wrappers.lambdaUpdate(UserDO.class)
                .eq(UserDO::getEmail, email);

        if(requestParam.getUsername() == null){
            requestParam.setUsername(userDO.getUsername());
        }
        if(requestParam.getPassword() == null){
            requestParam.setPassword(userDO.getPassword());
        }
        if(requestParam.getEmail() != null && !email.equals(requestParam.getEmail())){
            logout(email, token);
        }else{
            requestParam.setEmail(email);
        }

        try{
            update(BeanUtil.copyProperties(requestParam, UserDO.class), wrapper);
        }catch (DuplicateKeyException ex){
            throw new ClientException("用户邮箱已经存在");
        }
    }

    @Override
    public UserLoginRespDTO login(UserLoginReqDTO requestParam) {
//        // 1、检验用户名和密码是否正确
        UserDO userDO = lambdaQuery()
                .eq(UserDO::getEmail, requestParam.getEmail())
                .eq(UserDO::getPassword, DigestUtils.md5DigestAsHex(requestParam.getPassword().getBytes()))
                .eq(UserDO::getDelFlag, 0)
                .one();
        if (userDO == null) {
            throw new ClientException(USER_LOGIN_NAME_PASS_ERROR);
        }
//
        String tokenKey = USER_LOGIN_KEY + userDO.getEmail();

        // 2、防止用户重复登录,已经登录了则返回原来的token
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(tokenKey);
        if(CollUtil.isNotEmpty(entries)){
            String token = entries.keySet().stream()
                    .findFirst()
                    .map(Object::toString)
                    .orElseThrow(() -> new ClientException(USER_LOGIN_ERROR));
            return new UserLoginRespDTO(token, userDO.getUsername());
        }

        // 3、生成token
        String uuid = UUID.randomUUID(false).toString();

        // 4、token 保存到缓存中,并设置30分钟过期时间
        stringRedisTemplate.opsForHash().put(tokenKey, uuid, JSONUtil.toJsonStr(userDO));
        stringRedisTemplate.expire(tokenKey, 7, TimeUnit.DAYS);

        return new UserLoginRespDTO(uuid, userDO.getUsername());
    }

    @Override
    public Boolean checkLogin(String email, String token) {
        return stringRedisTemplate.opsForHash().get(USER_LOGIN_KEY + email, token) != null;
    }

    @Override
    public void logout(String email, String token) {
        if(checkLogin(email, token)){
            stringRedisTemplate.delete(USER_LOGIN_KEY + email);
        }else{
            throw new ClientException(USER_NOT_LOGIN_ERROR);
        }
    }

    @Override
    public void roleUpdate(Integer roleId, String email) {
        if(roleId == null || roleId < 1 || roleId > 4){
            throw new ClientException("权限id不合法");
        }
        if(StrUtil.isBlank(email)){
            throw new ClientException("参数不合法");
        }

        UserDO operator = lambdaQuery().eq(UserDO::getEmail, UserContext.getUserEmail()).one();
        if(operator == null || operator.getRoleId() != 1){
            throw new ClientException("操作者不存在或者无相关权限");
        }
        UserDO userDO = lambdaQuery().eq(UserDO::getEmail, email).one();
        if(userDO == null){
            throw new ClientException(USER_EMAIL_NOT_EXIST_ERROR);
        }
        boolean flag = lambdaUpdate()
                .eq(UserDO::getEmail, email)
                .set(UserDO::getRoleId, roleId)
                .update();
        if(!flag){
            throw new ServiceException("更新失败");
        }
    }

    @Override
    public List<UserActualRespDTO> getAll() {
        String userEmail = UserContext.getUserEmail();
        UserDO userDO = lambdaQuery().eq(UserDO::getEmail, userEmail).one();
        if(userDO == null || userDO.getRoleId() != 1){
            throw new ClientException("用户不存在或者无相关权限");
        }
        List<UserDO> list = lambdaQuery().eq(UserDO::getDelFlag, 0).list();
        return BeanUtil.copyToList(list, UserActualRespDTO.class);
    }
}
